package org.wlgzs.office_exam_student.servlet;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.wlgzs.office_exam_student.entity.ChoiceQuestion;
import org.wlgzs.office_exam_student.entity.Exam;
import org.wlgzs.office_exam_student.entity.ExamQuestion;
import org.wlgzs.office_exam_student.entity.OperateQuestion;
import org.wlgzs.office_exam_student.entity.Score;
import org.wlgzs.office_exam_student.entity.User;
import org.wlgzs.office_exam_student.permission.Authentication;
import org.wlgzs.office_exam_student.service.office.FinishedCall;
import org.wlgzs.office_exam_student.service.office.OfficeConfig;
import org.wlgzs.office_exam_student.service.office.ResolveResult;
import org.wlgzs.office_exam_student.service.office.ResolveTask;

import com.songminju.jdagger.JDCheck;

/**
 * @author 孤城落寞 2018年2月3日
 *
 */
@WebServlet("/exam")
public class ExamServlet extends BaseServlet {
	//
	private static final long serialVersionUID = 1L;

	// 每道选择题的分数
	private final int choiceScore = 1;

	private final Map<Integer, List<ChoiceQuestion>> examToChoices = new ConcurrentHashMap<>();
	private final Map<Integer, List<OperateQuestion>> examToWord = new ConcurrentHashMap<>();
	private final Map<Integer, List<OperateQuestion>> examToExcel = new ConcurrentHashMap<>();
	private final Map<Integer, List<OperateQuestion>> examToPpt = new ConcurrentHashMap<>();
	private final String FAIL_TIMEOUT = "{\"code\":\"fail\",\"reason\":\"考试已经结束。\"}";
	private final String SUCCESS = "{\"code\":\"success\",\"reason\":\"提交成功。\"}";

	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		Timer timer = new Timer();
		// 因为虚拟机空闲内存足够大，所以用弱引用可能导致内存被过多占用，手动定时清理缓存
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				logger.debug("clear examQuestions.");
				examToChoices.clear();
				examToWord.clear();
				examToExcel.clear();
				examToPpt.clear();
			}
		}, 0, 300 * 1000);
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setHeader("Cache-Control", "no-cache");
		User user = Authentication.user();
		Exam exam = examService.getCurrExam(user);
		if (exam == null) {
			req.setAttribute("remind", "目前还没有考试");
			render("404");
			return;
		}
		int examId = exam.getId();
		boolean b = true;
		try {
			b = scoreService.hasScore(user.getId(), examId);
		} catch (Exception e) {
			e.printStackTrace();
			req.setAttribute("remind", "当前考试人数过多，请稍后再试。");
			render("404");
			return;
		}
		if(b) {
			req.setAttribute("remind", "您已经参加过这次考试。");
			render("404");
			return;
		}
		
		logger.debug("start exam " + examId);
		req.getSession().setAttribute("examId", examId);
		List<ChoiceQuestion> choices = examToChoices.get(examId);
		List<OperateQuestion> words = examToWord.get(examId);
		List<OperateQuestion> excels = examToExcel.get(examId);
		List<OperateQuestion> ppts = examToPpt.get(examId);
		if (JDCheck.hasNull(choices, words, excels, ppts)) {
			logger.debug("cache had been clear");
			getQuestions(examId);
			choices = examToChoices.get(examId);
			words = examToWord.get(examId);
			excels = examToExcel.get(examId);
			ppts = examToPpt.get(examId);
		}/*
		words = new ArrayList<OperateQuestion>(words);
		excels = new ArrayList<OperateQuestion>(excels);
		ppts = new ArrayList<OperateQuestion>(words);*/
		MockExamServlet.changeChoice(choices,"choiceImg?img=");

		// 选择题 随机
		Collections.shuffle(choices);
		// 留2秒的时间不告诉用户，让他在最后提示交卷的时候还能交卷
		long lastTime = (exam.getEndTime().getTime() - System.currentTimeMillis()) / 1000 -2;
		req.setAttribute("lastTime", String.valueOf(lastTime));
		req.setAttribute("exam", exam);
		req.setAttribute("choices", choices);
		req.setAttribute("words", words);
		req.setAttribute("excels", excels);
		req.setAttribute("ppts", ppts);
		render("exam");
		return;
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		User user = Authentication.user();
		Exam exam = examService.getCurrExam(user);
		if (exam == null) {
			renderString(FAIL_TIMEOUT);
			return;
		}

		List<ChoiceQuestion> choiceQuestions = examToChoices.get(exam.getId());
		List<OperateQuestion> words = examToWord.get(exam.getId());
		List<OperateQuestion> excels = examToExcel.get(exam.getId());
		List<OperateQuestion> ppts = examToPpt.get(exam.getId());
		if (JDCheck.hasNull(choiceQuestions, words, excels, ppts)) {
			getQuestions(exam.getId());
			choiceQuestions = examToChoices.get(exam.getId());
			words = examToWord.get(exam.getId());
			excels = examToExcel.get(exam.getId());
			ppts = examToPpt.get(exam.getId());
		}

		// 总分数
		int totalScore = 0;
		// 选择题
		for (ChoiceQuestion cq : choiceQuestions) {
			String answer = req.getParameter(String.valueOf(cq.getId()));
			if (answer == null)
				continue;
			if (answer.equalsIgnoreCase(cq.getAnswer())) {
				totalScore += choiceScore;
			}
		}

		Timestamp now = new Timestamp(new Date().getTime());
		final Score score = new Score(user.getId(), user.getName(), user.getClassesId(), user.getClasses(), exam.getId(),
				exam.getName(), exam.getStartTime(), now, totalScore);
		score.setChoiceScore(totalScore);
		int id = scoreService.insert(score);
		score.setId(id);
		logger.debug("insert score:{}",score);
		int size = words.size() + excels.size() + ppts.size();
		MyFinishedCall[] calls = new MyFinishedCall[3];
		String[] types = {"word","excel","ppt"};
		for(int i=0;i<3;i++){
            calls[i] =  new MyFinishedCall("word") {
                @Override
                public void onFinished(ResolveResult rr) {
                    synchronized (score){
                        if(rr.score < 0){
                            rr.score = 0;
                        }
                        if(TYPE_WORD.equals(type)){
                            score.setWordScore(rr.score);
                        }else if(TYPE_EXCEL.equals(type)){
                            score.setExcelScore(rr.score);
                        }else if(TYPE_PPT.equals(type)){
                            score.setPptScore(rr.score);
                        }
                        logger.debug("update score:{}",score);
                        score.setScore(score.getChoiceScore() + score.getWordScore() + score.getExcelScore() + score.getPptScore());
                        scoreService.update(score);
                    }
                }
            };
        }

		for (int i = 0; i < words.size(); i++) {
			OperateQuestion oq = words.get(i);
			ResolveTask task = newTask(oq, user.getId(), exam.getId(), i);

			task.setFinshedCall(calls[0]);
		}

		for (int i = 0; i < excels.size(); i++) {
			OperateQuestion oq = excels.get(i);
			ResolveTask task = newTask(oq, user.getId(), exam.getId(), i);
			task.setFinshedCall(calls[1]);
		}

		for (int i = 0; i < ppts.size(); i++) {
			OperateQuestion oq = ppts.get(i);
			ResolveTask task = newTask(oq, user.getId(), exam.getId(), i);
			task.setFinshedCall(calls[2]);
		}
		renderString(SUCCESS);
	}

	private void getQuestions(int examId) {
		logger.debug("get questions.");
		List<ExamQuestion> examQuestions = examQuestionService.getQuestions(examId);
		logger.debug(examQuestions.size() + "");
		List<OperateQuestion> words = new LinkedList<>();
		List<OperateQuestion> excels = new LinkedList<>();
		List<OperateQuestion> ppts = new LinkedList<>();
		StringBuilder choiceIds = new StringBuilder("(-1,");
		for (ExamQuestion examQuestion : examQuestions) {
			logger.debug(examQuestion.toString());
			if (ExamQuestion.TYPE_OPERATE.equals(examQuestion.getType())) {

				OperateQuestion operates = operateQuestionService.operateQuestion(examQuestion.getQuestionId());
				if (operates != null) {
					operates.setSourceUrl(OfficeConfig.renderFiles(operates.getSourceUrl()));
					
					if (OperateQuestion.TYPE_WORD.equals(operates.getType())) {
						words.add(operates);
					}

					if (OperateQuestion.TYPE_EXCEL.equals(operates.getType())) {
						excels.add(operates);
					}

					if (OperateQuestion.TYPE_PPT.equals(operates.getType())) {
						ppts.add(operates);
					}
				}
			} else if (ExamQuestion.TYPE_CHOICE.equalsIgnoreCase(examQuestion.getType())) {
				choiceIds.append(examQuestion.getQuestionId()).append(',');
			}
		}
		choiceIds.deleteCharAt(choiceIds.length() - 1);
		choiceIds.append(')');
		List<ChoiceQuestion> choices = choiceQuestionService.choiceQuestionsByIds(choiceIds.toString());
		logger.debug("cache exam questions:choice:{},word:{},excel:{},ppt:{}", choices.size(), words.size(),
				excels.size(), ppts.size());

		examToChoices.put(examId, choices);
		examToWord.put(examId, words);
		examToExcel.put(examId, excels);
		examToPpt.put(examId, ppts);
	}

	private ResolveTask newTask(OperateQuestion oq, int userId, int examId, int index) {

		ResolveTask task = null;
		if (oq.getType().equals("word")) {
			task = new ResolveTask(userId, OfficeConfig.generateKey(oq.getId()), OfficeConfig.FileType.DOCX,
					OfficeConfig.PaperType.TEST);
		}

		if (oq.getType().equals("ppt")) {
			task = new ResolveTask(userId, OfficeConfig.generateKey(oq.getId()), OfficeConfig.FileType.PPTX,
					OfficeConfig.PaperType.TEST);
		}

		if (oq.getType().equals("excel")) {
			task = new ResolveTask(userId, OfficeConfig.generateKey(oq.getId()), OfficeConfig.FileType.XLSX,
					OfficeConfig.PaperType.TEST);
		}

		task.setExamId(examId);
		task.setIndex(index);
		officeResolverService.addTask(task);
		return task;
	}

	abstract class MyFinishedCall implements FinishedCall{

		public static final String TYPE_WORD = "word";
		public static final String TYPE_EXCEL = "excel";
		public static final String TYPE_PPT = "ppt";

		protected String type ;
		public MyFinishedCall(String type){
			this.type = type;
		}
	}
}